package com.shanxia.codec;

import com.shanxia.codec.enums.DecodeEnum;
import lombok.Data;

import java.util.Arrays;
import java.util.Objects;

@Data
public class ProtocolDataBase {
    private DecodeEnum result;
    //起始域 固定为 0xAA 0xF5
    private static byte h1 = (byte) 0xAA;
    private static byte h2 = (byte) 0xF5;
    //不参与序列化，长度域 getBytes()中设置
    private transient int length;
    //不参与序列化，信息域-加密标志：代表此数据帧是否加密
    private transient byte infoByte;
    // 信息域的第8个bit【业务字段，不参与encode到字节数组中】
    private int sign;
    // 信息域的前4个bit【业务字段，不参与encode到字节数组中】
    private int mainProtocolVersion;
    //序列号域：报文的流水号
    private byte serialByte;
    //命令代码：即消息类型，代表不同功能要求，命令代码为奇数表示服务器下发的数据，命令代码为偶数表示充电桩上传给服务器的数据包
    private int cmd;
    //不参与序列化，encode时通过 getBytes()方法调用业务指令的getDataField()方法 设置该参数
    private transient byte[] dataField;
    //不参与序列化
    private transient byte check;

    public void copyDataFrom(ProtocolDataBase c) {
        this.result = c.result;
        //this.length = c.length;
        this.infoByte = c.infoByte;
        //this.cmd = c.cmd;
        this.serialByte = c.serialByte;
        //this.dataField = c.dataField;
    }

    public byte[] getDataField() {
        return dataField;
    }

    // encode
    public byte[] getBytes() {
        // 调用getDataField方法
        byte[] d = getDataField();
        int fieldLen = (Objects.isNull(d) ? 0 : d.length);
        int length = 9 + fieldLen;
        byte[] res = new byte[length];

        //3字节头
        res[0] = h1;
        res[1] = h2;
        // 指令长度
        Decoder.getBytesAsUint16(res, 2, length);

        infoByte = Decoder.getInfoByte(sign, mainProtocolVersion);
        res[4] = infoByte;
        res[5] = serialByte;

        //2字节功能码
        Decoder.getBytesAsUint16(res, 6, cmd);

        for (int i = 0; i < d.length; i++) {
            res[8 + i] = d[i];
        }
        // 计算范围包含从命令代码和数据域
        res[8 + d.length] = (byte) getCheck(res, 6, d.length + 2);
        return res;
    }

    // 字节数组转对象 目前的data数组是原始数组从第5位开始, data数组长度 + 4 = 原始数组长度
    public static ProtocolDataBase decodeData(byte[] data, int len) {

        ProtocolDataBase protocolDataBase = new ProtocolDataBase();

        protocolDataBase.result = DecodeEnum.Success;

        // 校验和，校验区域为命令代CMD+数据域
        byte check = (byte) getCheck(data, 2, len - 3);

        if (check != data[len - 1]) {
            protocolDataBase.result = DecodeEnum.CheckError;
            return protocolDataBase;
        }
        protocolDataBase.length = len;
        //格式校验通过，解析数据
        int offset = 0;
        protocolDataBase.infoByte = data[offset];
        protocolDataBase.sign = (data[offset] >> 7) & 0xFF;
        // 取低4位
        protocolDataBase.mainProtocolVersion = data[offset] & 0x0F;
        offset += 1;
        protocolDataBase.serialByte = data[offset];
        offset += 1;
        protocolDataBase.cmd = Decoder.getUint16(data, offset);
        offset += 2;

        protocolDataBase.dataField = Arrays.copyOfRange(data, offset, len - 1);
        protocolDataBase.check = data[len - 1];
        return protocolDataBase;
    }

    public static int getCheck(byte[] data, int startPos, int len) {
        if (data == null)
            return 0;        //没数据
        if (startPos + len > data.length)
            return 0;  //参数有问题
        int check = 0;
        for (int i = 0; i < len; i++) {
            check = check + data[startPos + i] & 0xFF;
        }
        return check & 0xFF;
    }

    public static void main(String[] args) {
        String[] testStrings = {"AA F5 19 00 04 00 05 00 00 00 00 00 01 0F 00 00 00 01 04 00 55 00 00 00 6F"};
        byte[] testData = Decoder.hexStringToByteArray(testStrings[0].replace(" ", ""));
        System.out.println(getCheck(testData, 6, testData.length - 7));
    }
}
